#include "xml_parse.h"

#include <string>
#include <iostream>

xml_node::xml_node()
{
	parent = NULL;
	first_child = NULL;
	last_child = NULL;
	next_sibling = NULL;
}

xml_node::xml_node(class xml_node& copy)
{
	parent = NULL;
	first_child = NULL;
	last_child = NULL;
	next_sibling = NULL;
	name = copy.name;
	type = copy.type;
	values = copy.values;
	key_order = copy.key_order;
}

xml_node::xml_node(class xml_node *p)
{
	set_parent(p);
	first_child = NULL;
	last_child = NULL;
	next_sibling = NULL;
}

int xml_node::get_level()
{
	// we suppose that the level of the root node is 0.
	int level = 0;
	class xml_node *p = parent;
	while (p != NULL) {
		level++;
		p = p->parent;
	}
	return level;
}

void xml_node::set_parent(class xml_node *p)
{
	// this is equivalent to p->add_child()
	parent = p;
	if (parent != NULL) {
		parent->add_child(this);
	}
}

void xml_node::add_sibling(class xml_node *s)
{
	if (s == NULL)
		return;

	s->next_sibling = next_sibling;
	s->parent = parent;
	if (parent != NULL) {
		if (parent->last_child->is_equal_to(this))
			parent->last_child = s;
	}
	next_sibling = s;
}

void xml_node::add_child(class xml_node *c)
{
	if (c != NULL) {
		c->parent = this;
	}
	if (last_child != NULL) {
		last_child->next_sibling = c;
		last_child = c;
	} else {
		first_child = c;
		last_child = c;
	}
}

void xml_node::remove_child(class xml_node *c)
{
	class xml_node *p = first_child;
	class xml_node *prev;
	if (c == NULL)
		return;
	// the first child is special~
	if (first_child->is_equal_to(c)) {
		first_child->remove_all_children();
		if (last_child == first_child)
			last_child = first_child->next_sibling;
		first_child = first_child->next_sibling;
		delete p;
		return;
	}
	prev = first_child;
	p = first_child->next_sibling;
	while (p != NULL) {
		if (p->is_equal_to(c)) {
			p->remove_all_children();
			if (last_child == p)
				last_child = p->next_sibling;
			prev->next_sibling = p->next_sibling;
			delete p;
			return;
		}
		prev = p;
		p = p->next_sibling;
	}
	return;
}

void xml_node::remove_all_children()
{
	class xml_node *p = first_child;
	class xml_node *tmp;

	while (p != NULL) {
		p->remove_all_children();
		tmp = p;
		p = p->next_sibling;
		delete tmp;
	}
	return;
}

NODE_TYPE xml_node::set_content(const string& c)
{
	string content = c.substr(1, c.length() - 2);
	int len = content.length();
	values.clear();
	while (!key_order.empty()) key_order.pop();

	// assign name
	// split params
	switch(content[0]) {
	case '?':
		// start node
		type = VERSION_NODE;
		name = content;
		break;
	case '!':
		if (content[1] == '-' && content[2] == '-') {
			// comment node
			type = COMMENT_NODE;
			name = content;
			break;
		}
		// fall through
	case '/':
		// end node
		type = END_NODE;
		name = content.substr(1, len - 1);
		break;
	default:
		if (content[len - 1] == '/') {
			type = CLOSED_NODE;
		} else {
			type = BEGIN_NODE;
		}
		string::const_iterator i;
		string word, key, val;

		i = content.begin();
		while (i != content.end() && isspace(*i)) i++;
		while (i != content.end() && !isspace(*i)) word += *(i++);
		if (!word.empty())
			name = word;

		while (i != content.end()) {
			word.clear();
			while (i != content.end() && isspace(*i)) i++;
			while (i != content.end() && !isspace(*i)) word += *(i++);
			if (!word.empty()) {
				int pos = word.find_first_of('=');
				if (pos > 0) {
					key = word.substr(0, pos);
					val = word.substr( pos + 1, word.length() - 1);
					if (val[0] == '\"' && val[val.length() - 1] == '\"')
						val = val.substr(1, val.length() - 2);
					if (values.find(key) == values.end())
						key_order.push(key);
					values[key] = val;
				}
			}
		}
		break;
	}
	return type;
}

void xml_node::get_val(const string& key, string& val)
{
	if (values.find(key) != values.end())
		val = values[key];
	else
		val.clear();
}

void xml_node::set_val(const string& key, const string& val)
{
	if (values.find(key) == values.end())
		key_order.push(key);
	values[key] = val;
}

bool xml_node::is_equal_to(class xml_node *n)
{
	if (n == this ||
		(n->parent == parent &&
		n->first_child == first_child &&
		n->next_sibling == next_sibling)
		)
		return true;
	else
		return false;
}

void xml_node::to_string(string& c)
{
	if (type == END_NODE)
		c = "</" + name;
	else
		c = "<" + name;

	queue<string> order(key_order);
	while (!order.empty()) {
		string key = order.front();
		c += " " + key + "=\"" + values[key] + "\"";
		order.pop();
	}

	if (type == CLOSED_NODE)
		c += " />";
	else
		c += ">";
}

void xml_node::tree_to_string(string& content)
{
	string node, tab;
	xml_node *p = this;
	int level = get_level() - 1;

	if (level < 0) {
		p = p->first_child;
		level++;
	}
	tab = string(level * 2, ' ');
	while (p != NULL) {
		p->to_string(node);
		content += tab;
		content += node;
		content += '\n';
		if (p->first_child) {
			p->first_child->tree_to_string(content);
		}
		p = p->next_sibling;
	}
}

file_base::file_base(const string& file_name)
{
	h_file = CreateFile(file_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h_file == INVALID_HANDLE_VALUE) {
		cout << "open file " << file_name << " failed\n";
	}
	cur_pos = 0;
	num_read = 0;
}

int file_line::get_line(string& line)
{
	line.clear();

	// read the buffer first
	do {
		while (cur_pos < num_read) {
			if (buffer[cur_pos] == '\n') {
				// new line copied
				cur_pos++;
				return line.length();
			} else if (buffer[cur_pos] != '\r') {
				line.push_back(buffer[cur_pos]);
			}
			cur_pos++;
		}
		cur_pos = 0;
	} while (ReadFile(h_file, buffer, 1024, &num_read, NULL) && num_read > 0);
	
	return line.length();
}

int file_node::get_node(string& node)
{
	node.clear();
	bool new_node_flag = false;
	// read the buffer first
	do {
		while (new_node_flag == false) {
			while (cur_pos < num_read && buffer[cur_pos] != NODE_BEGIN) cur_pos++;
			if (cur_pos >= num_read)
				break;
			else
				new_node_flag = true;
		}
		while (cur_pos < num_read) {
			if (buffer[cur_pos] == NODE_END) {
				// new node copied
				node.push_back(NODE_END);
				cur_pos++;
				return node.length();
			} else if (buffer[cur_pos] != '\r' && buffer[cur_pos] != '\n') {
				node.push_back(buffer[cur_pos]);
			}
			cur_pos++;
		}
		cur_pos = 0;
	} while (ReadFile(h_file, buffer, 1024, &num_read, NULL) && num_read > 0);
	
	// reach the end of the file
	// got an incomplete node, which is invalid
	node.clear();
	return node.length();
}

xml_parser::xml_parser(const string& in_file_name)
{
	string node;
	xml_node *last, *parent, *n;
	NODE_TYPE node_type;

	file_node fn(in_file_name);
	
	root = new xml_node();
	if (root == NULL)
		return;

	last = root;
	while(fn.get_node(node)) {
		// cout << "node" << ": " << node << endl;
		n = new xml_node();
		if (n == NULL)
			return;
		node_type = n->set_content(node);

		switch (node_type) {
		case END_NODE:
			parent  = last->get_parent();
			if (!parent)
				parent = root;
			parent->add_sibling(n);
			break;
		default:
			if (last == root || last->get_type() == BEGIN_NODE)
				last->add_child(n);
			else
				last->add_sibling(n);
			break;
		}
		last = n;
		//n->to_string(node);
		//cout << "node" << i++ << ": " << node << endl;
	}
}

xml_parser::~xml_parser()
{
	// free the whole tree
	if (root != NULL) {
		root->remove_all_children();
		delete root;
	}
}

void xml_parser::find_node_byname(xml_node *node, const string& name, NL& node_list)
{
	if (node) {
		if (node->get_name() == name) {
			node_list.push_back(node);
		}
		find_node_byname(node->get_first_child(), name, node_list);
		find_node_byname(node->get_sibling(), name, node_list);
	}
}

int xml_parser::write_xml(const string& out_file_name)
{
	string tree;
	if (root && root->get_first_child())
		root->get_first_child()->tree_to_string(tree);

	HANDLE h_out_file = CreateFile(out_file_name.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h_out_file == INVALID_HANDLE_VALUE) {
		cout << "Error: create file " << out_file_name << " failed\n";
		return -1;
	}
	
	DWORD num_written;
	WriteFile(h_out_file, tree.data(), tree.length(), &num_written, NULL);
	if (num_written != tree.length()) {
		cout << "Error: WriteFile error!\n";
		CloseHandle(h_out_file);
		return -1;
	}
	
	CloseHandle(h_out_file);
	return 0;
}

